VIM 转义问题字符
1. 转义问题字符
::: alert-info
\V原义开关使得按原义查找文本变得更容易,因为符号 .、+ 以及 * 的特殊含义被屏蔽掉了。但还有一些字符,其特殊含义无法被屏蔽。本节作为高级技巧,将研究如何处理这些字符。
:::
1. 正向查找时要转义 / 字符
以下文本摘录自一篇Markdown文档。
patterns/search-url.markdown
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
只要光标位于方括号之中,就都可以使用命令 "uyi[
(为了方便记忆,用u表示URL)将此URL复制到寄存器u。然后输入 /\V<C-r>u<CR>
,即可将此寄存器的内容填充至查找域了。最终的查找提示符类似于这样。
➾ /\Vhttp://vimdoc.net/search?q=/\\
当执行此查找命令时,会得到以下结果。
Search items: [http: //vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
这到底是怎么回事?当我们把完整的URL粘贴至查找域后,Vim却把首次出现的符号 / 解析成了查找域结束符(参见查找域结束符)。因此,所有位于首个正斜杠之后的内容都被忽略掉了,所以查找字符串仅剩下了 http:
。
进行正向查找时,必须要转义符号 /。而且无论执行的是very magic
查找(使用模式开关 \v
)还是 very nomagic
查找(使用原义开关 \V
),都需要转义。修正一下之前的查找命令,为每个 / 加上反斜杠前缀。
➾ /\Vhttp:\/\/vimdoc.net\/search?q=\/\\
这一次查找的结果与我们预期的更为接近了。
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
但还不算完美,因为匹配的结果缺少了最后一个反斜杠。我们将很快揭晓其中的奥秘,但是首先考虑一下反向查找吧。
2. 反向查找时要转义?号
执行反向查找时,符号 ? 会被当作查找域的结束符。这意味着不必转义符号 / 了,但要对符号 ? 进行转义。
注意:
如果对从寄存器u复制而来的URL进行反向查找时,会发生什么事情。
➾ ?http://vimdoc.net/search?q=/\\
在没有转义任何内容之前,Vim将匹配字符串“http://vimdoc.net/search”。
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
这个结果比未转义过的正向查找要好多了,但仍未匹配完整的URL。如果将?号加上反斜杠前缀,会得到更好的结果。
➾ ?http://vimdoc.net/search\?q=/\\
以下是新的匹配结果。
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
3. 每次都要转义符号 \
在查找域中,还有一个字符需要转义,即反斜杠。通常情况下,一个 \
的出现预示着紧挨着它后面的字符将会得到某种特殊对待。如果将其加倍,变为 \\
后,前者会消除后者的特殊含义。实际上是让Vim查找一个反斜杠。
在我们的示例文本中,要查找的URL包含两个连续的反斜杠,因此,必须在查找域中为每个反斜杠各添加一个反斜杠。在正向查找中,我们最终得这样:
➾ /\Vhttp:\/\/vimdoc.net\/search?q=\/\\\\
完工!查询条件终于匹配了整个URL。
Search items: [http://vimdoc.net/search?q=/\\][s]
...
[s]: http://vimdoc.net/search?q=/\\
无论采用的是正向还是反向查找方式,反斜杠字符永远都需要转义。
4. 用编程的方式转义字符
用手动方式转义字符既耗时费力,又容易出错。幸运的是,Vim脚本提供了一个库函数,帮助我们完成这项艰巨的任务,即 escape({string}, {chars})
(参见 :h escape()
!
{chars}
参数将指定哪些字符需要用反斜杠转义。如果要进行正向查找,可以调用 escape(@u, '/\')
,它会为每个 /
与 \
加上反斜杠前缀。但如果进行的是反向查找,则要换用 escape(@u,'?\')
。
首先,要确保要查找的URL仍保存在寄存器 u
中。然后,输入 /
或者 ?
调出查找提示符,二者均能正确工作。最后,要依次输入原义开关 \V
与 <C-r>=
。在完成以上操作后,Vim就会从查找提示符的状态切换到表达寄存器提示符的状态了。现在输入以下命令。
➾ =escape(@u, getcmdtype().'\')
按下 <CR>
后,escape()
函数将被执行,其返回值将会被插入查找域。如果正在进行正向查找,getcmdtype()
函数只是简单地返回符号 /
,而在反向查找时,该函数将返回符号 ?
(参见 :h getcmdtype()
!)。在Vim脚本中,. 操作符用来连接字符串,因此,在正向查找时,getcmdtype()
.'\'
将产生“/\
”,而在反向查找时,会得到“?\
”。最终结果表明,无论采用哪种查找方式,表达式都将对寄存器 u
中的所有内容进行转义,因此查找工作顺利结束。
切换至表达式寄存器的状态以及手动调用 escape()
函数,仍然会涉及很多输入。如果再多用一点Vim脚本,操作即可实现自动化,使人们用起来更方便。请跳到技巧87,参考其中的实例。
查找域结束符
你可能会觉得奇怪,为什么查找域会把某个字符视为结束符呢?它为什么不把所有位于查找提示符之后的内容都纳入查找匹配呢?答案是如果在查找域结束符之后附加某些标志位,可以调整Vim查找命令的行为。例如,如果运行命令
/vim/e<CR>
,光标将会移到每个匹配的结尾,而非起始。在技巧83中,将学习如何利用此功能,而不是被其左右。还有一种输入模式的方式,让我们不必担心查找域结束符的牵绊,但它只能用于GVim,即使用
:promptfind
命令(参见:h :promptfind
!。该命令会调出一个带有“查找”标签的图形对话框,可以在这里输入 / 与?
字符而无需转义。遗憾的是,字符\
以及换行符依然会引发问题。